/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <string.h>
#include <stdio.h>

#include "crypt_modes_cbc.h"
#include "modes_local.h" 
#include "crypt_errno.h"
 
#ifdef __FRAMAC__
#include "__fc_builtin.h"

void *BSL_SAL_Calloc(uint32_t num, uint32_t size) {
    return Frama_C_alloc_by_stack((size_t)num * (size_t)size);
}

// 用于错误上报的存根。本次分析中更关注成功的执行路径，因此可以将其模型化为一个空操作。
void BSL_ERR_PUSH_ERROR(int err_code) {
    return;
}

// 用于 free 函数的存根。
void BSL_SAL_Free(void *ptr) {
    return;
}
#endif __FRAMAC__



int run_sm4_cbc_test(void) {
    printf("--- 开始 SM4-CBC 端到端测试 ---\n");

    // SM4 的密钥和 IV 必须是 16 字节（128 位）。
    const uint8_t key[16] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10
    };
    const uint8_t iv[16] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10
    };

    // 使用一个长度不是块大小（16字节）整数倍的明文，以确保填充机制得到测试。
    const char* plaintext_str = "这是一条用于测试SM4-CBC模式的机密消息。"; // 示例中文消息
    const uint8_t* plaintext = (const uint8_t*)plaintext_str;
    const uint32_t plaintext_len = strlen(plaintext_str);

    // 用于输出的缓冲区。大小必须足以容纳明文 + 填充。
    // 对于一个给定长度的输入，填充后的长度会向上取整到16的倍数。
    uint8_t ciphertext[256];
    uint8_t recovered_plaintext[256];

    // 用于追踪输出长度的变量
    uint32_t ciphertext_len = 0;
    uint32_t temp_out_len = 0;
    uint32_t recovered_len = 0;

    int32_t ret;

    printf("加密中...\n");

    // 步骤 1：创建并初始化上下文
    MODES_CipherCtx *ctx_enc = MODES_CBC_NewCtx(CRYPT_CIPHER_SM4_CBC);
    if (ctx_enc == NULL) {
        printf("错误：创建加密上下文失败。\n");
        return -1;
    }

    ret = SM4_CBC_InitCtx(ctx_enc, key, sizeof(key), iv, sizeof(iv), true); // true 代表加密
    if (ret != CRYPT_SUCCESS) {
        printf("错误：初始化加密上下文失败。\n");
        MODES_CBC_FreeCtx(ctx_enc);
        return -1;
    }

    // 显式设置填充模式。PKCS7 是标准模式。
    int32_t padding = CRYPT_PADDING_PKCS7;
    MODES_CBC_Ctrl(ctx_enc, CRYPT_CTRL_SET_PADDING, &padding, sizeof(padding));

    // 步骤 2：处理明文数据
    ret = SM4_CBC_Update(ctx_enc, plaintext, plaintext_len, ciphertext, &temp_out_len);
    if (ret != CRYPT_SUCCESS) {
        printf("错误：加密更新操作失败。\n");
        MODES_CBC_FreeCtx(ctx_enc);
        return -1;
    }
    ciphertext_len += temp_out_len;

    // 步骤 3：完成加密 (应用填充并加密最后一个块)
    ret = SM4_CBC_Final(ctx_enc, ciphertext + ciphertext_len, &temp_out_len);
    if (ret != CRYPT_SUCCESS) {
        printf("错误：加密终结操作失败。\n");
        MODES_CBC_FreeCtx(ctx_enc);
        return -1;
    }
    ciphertext_len += temp_out_len;

    printf("加密成功。明文长度: %u, 密文长度: %u\n", plaintext_len, ciphertext_len);

    // 清理加密上下文
    MODES_CBC_FreeCtx(ctx_enc);


    // 解密阶段

    printf("解密中...\n");
    temp_out_len = 0; // 为解密重置计数

    // 步骤 1：创建并初始化上下文
    MODES_CipherCtx *ctx_dec = MODES_CBC_NewCtx(CRYPT_CIPHER_SM4_CBC);
    if (ctx_dec == NULL) {
        printf("错误：创建解密上下文失败。\n");
        return -1;
    }

    ret = SM4_CBC_InitCtx(ctx_dec, key, sizeof(key), iv, sizeof(iv), false); // false 代表解密
    if (ret != CRYPT_SUCCESS) {
        printf("错误：初始化解密上下文失败。\n");
        MODES_CBC_FreeCtx(ctx_dec);
        return -1;
    }

    // 设置相同的填充类型以便正确地移除填充
    MODES_CBC_Ctrl(ctx_dec, CRYPT_CTRL_SET_PADDING, &padding, sizeof(padding));

    // 步骤 2：处理密文数据
    ret = SM4_CBC_Update(ctx_dec, ciphertext, ciphertext_len, recovered_plaintext, &temp_out_len);
    if (ret != CRYPT_SUCCESS) {
        printf("错误：解密更新操作失败。\n");
        MODES_CBC_FreeCtx(ctx_dec);
        return -1;
    }
    recovered_len += temp_out_len;

    // 步骤 3：完成解密 (解密最后一个块并移除填充)
    ret = SM4_CBC_Final(ctx_dec, recovered_plaintext + recovered_len, &temp_out_len);
    if (ret != CRYPT_SUCCESS) {
        printf("错误：解密终结操作失败。\n");
        MODES_CBC_FreeCtx(ctx_dec);
        return -1;
    }
    recovered_len += temp_out_len;

    printf("解密成功。恢复的明文长度: %u\n", recovered_len);

    // 清理解密上下文
    MODES_CBC_FreeCtx(ctx_dec);


    printf("验证中...\n");



    // 标准的 C 语言验证
    if (recovered_len != plaintext_len) {
        printf("失败：长度不匹配！原始长度: %u, 恢复后长度: %u\n", plaintext_len, recovered_len);
        return -1;
    }

    if (memcmp(plaintext, recovered_plaintext, plaintext_len) != 0) {
        printf("失败：内容不匹配！\n");
        return -1;
    }

    printf("--- 成功：测试通过！恢复的明文与原始明文完全匹配。 ---\n");
    return 0;
}

// 可执行程序的主入口点
int main(void) {
    int result = run_sm4_cbc_test();
    if (result == 0) {
        printf("测试驱动成功结束。\n");
    } else {
        printf("测试驱动因错误结束。\n");
    }
    return result;
}
